package com.imooc.zhousc.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.Collections;
import java.util.List;

@Slf4j
public class ZookeeperLock implements AutoCloseable, Watcher {

    private ZooKeeper zooKeeper;

    private String zookNode;
    
    public ZookeeperLock(){
        try {
            zooKeeper = new ZooKeeper("localhost:2181",50000,this);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public boolean getLock(String businessKey){
        // 先看持久节点是否存在
        try {
            Stat exists = zooKeeper.exists("/" + businessKey, false);
            if (exists == null){
                // 需要创建根节点
                zooKeeper.create("/" + businessKey, businessKey.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
            }
            // 创建瞬时节点
            zookNode = zooKeeper.create("/" + businessKey + "/" + businessKey + "_", businessKey.getBytes(),
                    ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            List<String> children = zooKeeper.getChildren("/" + businessKey, false);
            Collections.sort(children);
            String fristNode = children.get(0);
            if (zookNode.endsWith(fristNode)){
                return true;
            }

            String lastNode = fristNode;
            for (String node : children) {
                if (zookNode.endsWith(node)){
                    // 监听上一个节点
                    zooKeeper.exists("/" + businessKey + "/" + lastNode, true);
                }else {
                    lastNode = node;
                }
            }
            // 等待监听上一个节点被删除，会唤醒当前节点
            synchronized (this){
                wait();
            }
            return true;
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * 释放锁
     *
     * @throws Exception
     */
    @Override
    public void close() throws Exception {
        zooKeeper.delete(zookNode,-1);
        zooKeeper.close();
        log.info("释放锁"+ zookNode);
    }

    @Override
    public void process(WatchedEvent watchedEvent) {
        // 当监听上一个节点为删除节点事件时触发
        if(watchedEvent.getType() == Event.EventType.NodeDeleted){
            synchronized (this){
                notify();
            }
        }
    }
}
